package com.black.utils.network.app.sftp;

import com.black.utils.commons.pool.PoolProperties;
import com.jcraft.jsch.*;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.util.Properties;

import static com.black.utils.commons.constant.JSchConstant.SFTP;
import static com.black.utils.file.FileUtil.BUFFER_SIZE;

/**
 * @author ylx
 */
@Slf4j
@NoArgsConstructor
public class SFTPClient
{
    private Session session;

    private ChannelSftp channel;

    @Setter
    private PoolProperties sftpProperties;

    private Properties config = new Properties();

    public SFTPClient(PoolProperties sftpProperties)
        throws JSchException
    {
        this.sftpProperties = sftpProperties;
        config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        connect();
    }

    public void connect()
        throws JSchException
    {
        JSch jsch = new JSch();
        session = jsch.getSession(sftpProperties.getAccount(), sftpProperties.getIp(), sftpProperties.getPort());
        session.setPassword(sftpProperties.getPassword());
        session.setConfig(config);
        session.connect();
        channel = (ChannelSftp)session.openChannel(SFTP);
        channel.connect();
    }

    public boolean upload(String localPath, String remotePath)
    {
        try (FileInputStream inputStream = new FileInputStream(localPath);
             OutputStream put = channel.put(remotePath))
        {
            byte[] buffer = new byte[BUFFER_SIZE << 5];
            int length;
            while ((length = inputStream.read(buffer)) != -1)
            {
                put.write(buffer, 0, length);
            }
            put.flush();
            return true;
        }
        catch (SftpException e)
        {
            log.error("put error", e);
            return false;
        }
        catch (FileNotFoundException e)
        {
            log.error("file not found", e);
            return false;
        }
        catch (IOException e)
        {
            log.error("io error", e);
            throw new RuntimeException(e);
        }
    }

    public boolean download(String remotePath, String localPath)
    {
        try (InputStream inputStream = channel.get(remotePath);
             OutputStream outputStream = new FileOutputStream(localPath))
        {
            byte[] buffer = new byte[BUFFER_SIZE << 5];
            int length;
            while ((length = inputStream.read(buffer)) != -1)
            {
                outputStream.write(buffer, 0, length);
            }
            outputStream.flush();
            return true;
        }
        catch (SftpException e)
        {
            log.error("put error", e);
            return false;
        }
        catch (FileNotFoundException e)
        {
            log.error("file not found", e);
            return false;
        }
        catch (IOException e)
        {
            log.error("io error", e);
            throw new RuntimeException(e);
        }
    }

    public boolean delete(String remotePath)
    {
        try
        {
            channel.rm(remotePath);
            return true;
        }
        catch (SftpException e)
        {
            log.error("delete error", e);
            return false;
        }
    }

    public boolean mkdir(String remotePath)
    {
        try
        {
            channel.mkdir(remotePath);
            return true;
        }
        catch (SftpException e)
        {
            log.error("mkdir error", e);
            return false;
        }
    }

    public boolean isAvailable()
    {
        return session != null && channel != null;
    }

    public boolean isConnected()
    {
        return isAvailable() && session.isConnected() && channel.isConnected();
    }

    public void reConnect()
    {
        try
        {
            close();
            connect();
        }
        catch (JSchException e)
        {
            throw new RuntimeException(e);
        }
    }

    public void close()
    {
        if (channel != null)
        {
            channel.disconnect();
        }
        if (session != null)
        {
            session.disconnect();
        }
    }
}
